#!/usr/bin/env python
# -*- coding: utf-8 -*-


from queue import Queue, Empty
import serial
import time
from threading import Thread, Lock
import datetime

from .tools import singleton
from .tools import to_hex_list
from .tools import Timer2

import random


import logging  # 引入logging模块
logger = logging.getLogger()


# from queue import Queue, Empty


@singleton
class Txd(Thread):
    # 串口发送线程
    def __init__(self, dev=None, host=None):
        # 参数: 串口号, 主机还是从机
        Thread.__init__(self)
        self.queue = Queue()

        # 打开串口
        com = serial.Serial(dev, 115200, timeout=0.2)

        com.isOpen()
        self.com = com
        self.host = host
        self.lock = Lock()
        self.lockedstart()

    def lockedstart(self):
        with self.lock:
            if not self.isAlive():
                self.daemon = True
                self.start()

    def run(self):
        while True:
            try:
                # 等待发送队列
                cmd = self.queue.get(True)
                # print(datetime.datetime.now(), self.host, ' txd run,',  cmd)
                logger.debug('------len=%s, cmd=%s' %
                             (len(cmd), to_hex_list(cmd)))

                # 写入串口
                self.com.write(cmd)

                # time.sleep(3)
                pass
            except Exception as e:
                logger.debug('Error: %s' % str(e))
                # print('Error: %s' % str(e))

    def format_command(self, cmd_word, cmd_data):
        # 串口协议规约
        # 起始标志(2) + 命令字(2) + 长度n(1) + 数据(n) + 校验和(3)
        # 起始标志 上行 0xAA55, 下行 0xAA55
        # 命令字 2字节
        # 长度n 后续数据的长度
        # 数据
        # 校验和,  前面 n + 6 字节的和

        first = self.host == 'host' and [0xAA, 0x55] or [0x55, 0xAA]
        cmd = [cmd_word // 256, cmd_word % 256]
        data_len = len(cmd_data)
        lens = [data_len // 256, data_len % 256]
        to_send = first + cmd + lens + cmd_data
        cs = sum(to_send) % 0x1000000

        to_send2 = to_send + [cs // 0x10000,
                              (cs % 0x10000) // 0x100, cs % 0x100]

        return to_send2

    def txd_data(self, cmd_word, cmd_data):
        # 对外的接口函数
        # 参数: 命令字 + 数据
        # print('txd_data', self.host, cmd_word, cmd_data)

        self.lockedstart()

        to_send = self.format_command(cmd_word, cmd_data)
        self.queue.put(to_send)

        # while to_send:
        #     cnt = random.randint(5, 10)
        #     be_send = to_send[0: 14]
        #     to_send = to_send[14:]
        #     self.queue.put(be_send)
        #     time.sleep(random.randint(600, 1200) / 1000)


WAIT_FOR_FIRST1 = 1
WAIT_FOR_FIRST2 = 2
WAIT_FOR_CMD1 = 3
WAIT_FOR_CMD2 = 4
WAIT_FOR_LEN1 = 5
WAIT_FOR_LEN2 = 6
WAIT_FOR_DATA = 7
WAIT_FOR_CS1 = 8
WAIT_FOR_CS2 = 9
WAIT_FOR_CS3 = 10


def process_char(self, state, ch, first):
    # 第1步找 起始标志 第1字节
    if state['wait'] == WAIT_FOR_FIRST1:
        if ch == first[0]:
            return {
                'done': [ch],
                'wait': WAIT_FOR_FIRST2,
                'redo': [],
                'commands': state['commands'],
                'sum': ch,
                'wait_for_len': 100000 * WAIT_FOR_FIRST2
            }

        else:
            # 不是开始标志, 丢弃
            return {
                'done': [],
                'wait': WAIT_FOR_FIRST1,
                'redo': [],
                'commands': state['commands'],
                'sum': 0,
                'wait_for_len': 100000 * WAIT_FOR_FIRST1
            }

    # 第2步找 起始标志 第2字节
    elif state['wait'] == WAIT_FOR_FIRST2:
        if ch == first[1]:
            return {
                'done': state['done'] + [ch],
                'wait': WAIT_FOR_CMD1,
                'redo': state['redo'] + [ch],
                'commands': state['commands'],
                'sum': state['sum'] + ch,
                'wait_for_len': 100000 * WAIT_FOR_CMD1
            }

        else:
            # error , 出错了 重新判断
            return None

    # 第3步 命令字 第1字节
    elif state['wait'] == WAIT_FOR_CMD1:
        return {
            'done': state['done'] + [ch],
            'wait': WAIT_FOR_CMD2,
            'redo': state['redo'] + [ch],
            'commands': state['commands'],
            'sum': state['sum'] + ch,
            'cmd': ch * 256,
            'wait_for_len': 100000 * WAIT_FOR_CMD2
        }

    # 第4步 命令字 第2字节
    elif state['wait'] == WAIT_FOR_CMD2:
        return {
            'done': state['done'] + [ch],
            'wait': WAIT_FOR_LEN1,
            'redo': state['redo'] + [ch],
            'commands': state['commands'],
            'sum': state['sum'] + ch,
            'cmd': state['cmd'] + ch,
            'wait_for_len': 100000 * WAIT_FOR_LEN1
        }

    # 第5步 长度 第1字节
    elif state['wait'] == WAIT_FOR_LEN1:
        return {
            'done': state['done'] + [ch],
            'wait': WAIT_FOR_LEN2,
            'redo': state['redo'] + [ch],
            'commands': state['commands'],
            'sum': state['sum'] + ch,
            'cmd': state['cmd'],
            'len': ch * 256,
            'wait_for_len': 100000 * WAIT_FOR_LEN2
        }

    # 第6步 长度 第2字节
    elif state['wait'] == WAIT_FOR_LEN2:

        my_len = state['len'] + ch
        # logger.info('rxd, total data length=%s', my_len)

        if my_len > 0:
            # 长度字节 不为 0
            return {
                'done': state['done'] + [ch],
                'wait': WAIT_FOR_DATA,
                'redo': state['redo'] + [ch],
                'commands': state['commands'],
                'sum': state['sum'] + ch,
                'cmd': state['cmd'],
                'len': my_len,
                'data': [],
                'wait_for_len': 100000 * WAIT_FOR_DATA + my_len
            }
        else:
            # 长度字节为 0 直接 到 最后一步
            return {
                'done': state['done'] + [ch],
                'wait': WAIT_FOR_CS1,
                'redo': state['redo'] + [ch],
                'commands': state['commands'],
                'sum': state['sum'] + ch,
                'cmd': state['cmd'],
                'len': my_len,
                'data': [],
                'wait_for_len': 100000 * WAIT_FOR_CS1

            }

    # 第7步 按照长度 获取数据
    elif state['wait'] == WAIT_FOR_DATA:
        my_len = state['len'] - 1

        if my_len > 0:
            # 还有
            return {
                'done': state['done'] + [ch],
                'wait': WAIT_FOR_DATA,
                'redo': state['redo'] + [ch],
                'commands': state['commands'],
                'sum': state['sum'] + ch,
                'cmd': state['cmd'],
                'len': my_len,
                'data': state['data'] + [ch],
                'wait_for_len': 100000 * WAIT_FOR_DATA + my_len
            }
        else:
            # 数据收完了
            return {
                'done': state['done'] + [ch],
                'wait': WAIT_FOR_CS1,
                'redo': state['redo'] + [ch],
                'commands': state['commands'],
                'sum': state['sum'] + ch,
                'cmd': state['cmd'],
                'data': state['data'] + [ch],
                'cs': 0,
                'wait_for_len': 100000 * WAIT_FOR_CS1
            }

    # 第8步 校验和 第1字节
    elif state['wait'] == WAIT_FOR_CS1:
        # print('my sum, ', state['sum'])

        return {
            'done': state['done'] + [ch],
            'wait': WAIT_FOR_CS2,
            'redo': state['redo'] + [ch],
            'commands': state['commands'],
            'sum': state['sum'],
            'cmd': state['cmd'],
            'data': state['data'],
            'len': 0,
            'cs': state['cs'] * 0x100 + ch,
            'wait_for_len': 100000 * WAIT_FOR_CS2
        }

    # 第9步 校验和 第2字节
    elif state['wait'] == WAIT_FOR_CS2:
        return {
            'done': state['done'] + [ch],
            'wait': WAIT_FOR_CS3,
            'redo': state['redo'] + [ch],
            'commands': state['commands'],
            'sum': state['sum'],
            'cmd': state['cmd'],
            'data': state['data'],
            'cs': state['cs'] * 0x100 + ch,
            'wait_for_len': 100000 * WAIT_FOR_CS3
        }

    # 第9步 校验和 第3字节
    elif state['wait'] == WAIT_FOR_CS3:
        cs = state['cs'] * 0x100 + ch
        sum2 = state['sum'] % 0x1000000

        # print( 'sum2, cs:',  sum2, cs )

        if sum2 == cs:
            # 一切 ok,
            return {
                'done': [],
                'wait': WAIT_FOR_FIRST1,
                'redo': [],
                'commands': state['commands'] + [(state['cmd'], state['data'])],
                'sum': 0,
                'wait_for_len': 100000 * WAIT_FOR_FIRST1
            }
        else:  # error , 出错了 重新判断
            return None

    return None


def process_multi_char(self, state, todo0, first):
    # 参数:
    # 原状态
    # 待处理的 数据 list
    # 头字节格式, 上行 下行 定义不一样

    todo = [e for e in todo0]
    # print('process_multi_char2, state, todo, ', state, todo, first)

    new_state = state

    while True:
        # 如果都处理结束了, 返回
        if not todo:
            # print('process_multi_char2, ret, state, ', new_state)
            # logger.info('rxd, some data, state=%s', new_state)
            return new_state

        # 暂存 原有状态
        old_state = new_state

        # 取一个字节
        ch = todo.pop(0)

        # print('old_state, ch', old_state, ch, )

        # 处理这一个字节
        new_state = process_char(self, old_state, ch, first)
        # print('new_state, ch', new_state, ch, )

        if not new_state:
            # error
            # 如果 这个字节不合法, 说明数据包格式错
            # 则 丢掉 头字节后, 重新找头字节
            todo = old_state['redo'] + [ch] + todo
            new_state = {
                'done':  [],
                'wait': WAIT_FOR_FIRST1,
                'redo': [],
                'commands': old_state['commands'],
                'sum': 0,
                'wait_for_len': 100000 * WAIT_FOR_FIRST1

            }
            # print(', error redo, new_state, new todo', new_state, todo )

    # print('process_multi_char2, ret, new_state, ', new_state)

    return new_state


@singleton
class Rxd(Thread):
    def __init__(self, dev=None, host=None, msg=None):
        # 串口接收 线程
        # 参数, 串口号, 主从机标志, 回调的 msg 对象

        Thread.__init__(self)
        self.lock = Lock()
        com = serial.Serial(dev, 115200, timeout=0.2)
        com.isOpen()
        self.com = com

        self.host = host
        self.msg = msg
        self.init_state()

        self.timer = None
        self.init_timer()
        self.lockedstart()

    def init_timer(self):
        if self.timer:
            self.timer.cancel()

        # logger.info('rxd, init timer')
        self.timer = Timer2(10, self.init_state_by_timeout)

    def init_state_by_timeout(self):
        logger.debug('rxd, init state by timeout')
        self.init_state()

    def init_state(self):
        # 初始化, 串口状态, 等待 头标志字节
        self.state = {
            'done':  [],
            'wait': WAIT_FOR_FIRST1,
            'redo': [],
            'commands': [],
            'wait_for_len': 100000 * WAIT_FOR_FIRST1
        }

    @property
    def state_num(self):
        return self.state and self.state.get('wait_for_len')

    @property
    def state_done(self):
        return self.state and self.state.get('done')

    def lockedstart(self):
        with self.lock:
            if not self.isAlive():
                self.daemon = True
                self.start()

    def run(self):
        while True:
            try:
                count = self.com.inWaiting()  # 获取串口缓冲区数据
                if count != 0:
                    # 从串口 收数据
                    chars = self.com.read(self.com.in_waiting)
                    self.rxd_data(chars)

                time.sleep(0.1)
                pass
            except Exception as e:
                logger.debug('Error: %s' % str(e))
                # print('Error: %s' % str(e))

    def rxd_data(self, chars):
        chars2 = [ch for ch in chars]
        logger.debug('+++++len=%s' % len(chars2))
        logger.debug('+++++cmd=%s' % to_hex_list(chars2))
        # logger.debug('+++++len=%s, cmd=%s' %
        #              (len(chars2), to_hex_list(chars2)))

        first = self.host == 'host' and [0x55, 0xAA] or [0xAA, 0x55]

        # 数据处理函数
        self.init_timer()
        state = process_multi_char(self, self.state, chars2, first)
        self.init_timer()

        for cmd, data in state['commands']:
            # print(self.host, 'rxd cmd, ', cmd, data)
            # 如果收到一个完整的命令, 回调消息对象
            self.msg.put(cmd,  [d for d in data])

        # 清空收到的消息
        state['commands'] = []

        # 更新状态
        self.state = state
        # print('self.state', self.state)
